package com.fulihui.wgj.consumer.communication.event;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSONObject;
import com.fulihui.common.constants.PlaceholderConstants;
import com.fulihui.common.enums.ContainOrEliminateEnum;
import com.fulihui.common.enums.CustomMsgTypeEnum;
import com.fulihui.common.enums.EventTypeEnum;
import com.fulihui.common.enums.SubReplyTypeEnum;
import com.fulihui.common.enums.SubReplyUseTypeEnum;
import com.fulihui.common.request.rpc.AppRequest;
import com.fulihui.common.result.rpc.SimpleResult;
import com.fulihui.common.util.JedisUtil;
import com.fulihui.common.util.RegHttpUtil;
import com.fulihui.common.util.SevenRemainingUtil;
import com.fulihui.common.util.TenMinutesMsgUtil;
import com.fulihui.open.shortlink.model.WgjSendLink;
import com.fulihui.wgj.consumer.config.property.ShortLinkProperties;
import com.fulihui.wgj.consumer.entity.WxUserInfoExp;
import com.fulihui.wgj.consumer.service.ExternalSystemService;
import com.fulihui.wgj.consumer.service.SubUserDatilService;
import com.fulihui.wgj.consumer.service.UserService;
import com.fulihui.wgj.consumer.service.WxMsgService;
import com.fulihui.wgj.facade.dto.NotifyUserActivityDTO;
import com.fulihui.wgj.facade.dto.WxQrcodeReplyInfoDTO;
import com.fulihui.wgj.facade.dto.WxSubscribeReplyAdvertDTO;
import com.fulihui.wgj.facade.dto.WxSubscribeReplyRuleDTO;
import com.fulihui.wgj.facade.dto.WxUserInfoDTO;
import com.fulihui.wgj.facade.msg.EventMsg;
import com.fulihui.wgj.facade.request.qrcode.WxQrcodeReplyQueryOneRequest;
import com.fulihui.wgj.facade.service.qrcode.WxParamQrcodeService;
import com.fulihui.wgj.facade.service.reply.WxSubscribeAdverseService;
import com.fulihui.wgj.facade.service.reply.WxSubscribeReplyService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.near.toolkit.common.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 微信事件消息：用户关注消息处理器
 *
 * @author: Levon
 * @version: v 0.1 2018-04-26 11:16
 */
@Component
@Slf4j
public class SubscribeEventProcessor implements EventMsgProcessor {

    @Autowired
    private UserService userService;
    @Autowired
    private ExternalSystemService externalSystemService;
    @Autowired
    private WxMsgService wxMsgService;
    @Autowired
    private JedisUtil jedisUtil;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Reference(version = "1.0.0")
    private WxParamQrcodeService wxParamQrcodeService;
    @Reference(version = "1.0.0")
    private WxSubscribeReplyService wxSubscribeReplyService;
    @Reference(version = "1.0.0")
    private WxSubscribeAdverseService adverseService;
    @Autowired
    private SubUserDatilService subUserDatilService;
    @Autowired
    private TenMinutesMsgUtil tenMinutesMsgUtil;
    @Autowired
    private SevenRemainingUtil sevenRemainingUtil;
    @Autowired
    private ShortLinkProperties shortLinkProperties;

    @Override
    public void process(EventMsg message) {
        log.info("subscribe event:message={}", message);

        String appId = message.getAppId();
        String openId = message.getFromUserName();
        String eventKey = message.getEventKey();

        if (StringUtils.isBlank(appId) || StringUtils.isBlank(openId)) {
            log.error("关注事件，关键字段为空，appId={},openid={}", appId, openId);
            return;
        }
        //回复状态 false:未回复、true：已经扫码回复
        boolean replyState = false;

        //如果是参数二维码扫描关注
        if (StringUtils.isNotBlank(eventKey) && eventKey.startsWith("qrscene_")) {
            //调用立白、参数二维码回复
            replyState = QRCodeReplay(message);
        }

        // 获取用户信息
        WxUserInfoExp userInfo = userService.getUserInfo(appId, openId);
        if (null == userInfo) {
            log.error("用户关注-用户信息为空！！！appid={},openId={}, return 未执行插入和关注回复操作！", appId, openId);
            return;
        }

        // 未进行二维码回复的状态下 关注回复
        if (!replyState) {
            threadPoolTaskExecutor.execute(() -> {
                subReply(userInfo);
            });
        }

        threadPoolTaskExecutor.execute(() -> {
            // upsert to Kudu
            userService.upsertUsers(Collections.singletonList(userInfo));

            // 10分钟拉活消息写入redis
            tenMinutesMsgUtil.recordSubscribe(appId, openId);
        });

        threadPoolTaskExecutor.execute(() -> {
            // 七日留存redis缓存近10天的用户关注时间
            sevenRemainingUtil.set(openId, userInfo.getSubscribeTime());

            //  * 针对当天取关又关注的情况，删除取关记录
            //  * 多次关注只记录1条关注数，
            //  * 所以，当天重新关注后，取关记录应该删除
            sevenRemainingUtil.removeUnsubscribeRecord(appId, System.currentTimeMillis(), openId);
        });

        // 记录用户关注情况 wx_sub_user_datil 用于为产品/运营导出报表,以及参数二维码统计关注、取关事件
        subUserDatilService.reocdeSubUserDatil(userInfo);
    }

    /**
     * 未进行二维码回复的状态下 进行 特定条件回复 或默认回复
     */
    private void subReply(WxUserInfoExp userInfo) {
        String appId = userInfo.getAppId();
        AppRequest request = new AppRequest();
        request.setAppId(appId);
        List<WxSubscribeReplyRuleDTO> rules = wxSubscribeReplyService.queryReplyRuleByAppIdUseful(request).getData();
        WxSubscribeReplyRuleDTO defaultRule = null;
        if (!CollectionUtils.isEmpty(rules)) {
            //循环所有的特定回复规则，先匹配到哪个就回复
            for (WxSubscribeReplyRuleDTO rule : rules) {
                if (SubReplyUseTypeEnum.SPECIFIC.getValue() == rule.getUseType()) {
                    if (checkRule(rule, userInfo)) {
                        sendSubReplayMsg(rule, userInfo);
                        return;
                    }
                } else if (SubReplyUseTypeEnum.DEFAULT.getValue() == rule.getUseType()) {
                    defaultRule = rule;
                }
            }
        }
        // 若特定关注回复也没匹配上就进行 默认回复
        if (null != defaultRule) {
            sendSubReplayMsg(defaultRule, userInfo);
        }
    }

    /**
     * 检查用户是否符合特定回复条件
     */
    private boolean checkRule(WxSubscribeReplyRuleDTO rule, WxUserInfoDTO userInfo) {
        // default true 只要有一个条件不符合就置为false
        boolean flag = true;
        //check sex
        Long sex = userInfo.getSex();
        Integer ruleSex = rule.getSex();
        if (Objects.nonNull(ruleSex)) {
            if (ContainOrEliminateEnum.CONTAIN.getCode().equals(rule.getSexTab())) {
                if (ruleSex != sex.intValue()) {
                    flag = false;
                }
            } else if (ContainOrEliminateEnum.ELIMINATE.getCode().equals(rule.getSexTab())) {
                if (ruleSex == sex.intValue()) {
                    flag = false;
                }
            } else {
                flag = false;
                log.error("关注回复 sex tab is null ! appId = {},ruleId = {}", rule.getAppId(), rule.getProvince());
            }
        }

        // check province
        String province = userInfo.getProvince();
        if (StringUtils.isNotBlank(rule.getProvince())) {
            List<String> ruleProvince = Arrays.asList(rule.getProvince().split(","));
            if (ContainOrEliminateEnum.CONTAIN.getCode().equals(rule.getProvinceTab())) {
                if (!ruleProvince.contains(province)) {
                    flag = false;
                }
            } else if (ContainOrEliminateEnum.ELIMINATE.getCode().equals(rule.getProvinceTab())) {
                if (ruleProvince.contains(province)) {
                    flag = false;
                }
            } else {
                flag = false;
                log.error("关注回复 province tab is null ! appId = {},ruleId = {}", rule.getAppId(), rule.getId());
            }
        }

        return flag;
    }

    private void sendSubReplayMsg(WxSubscribeReplyRuleDTO rule, WxUserInfoExp user) {
        Integer replyType = rule.getReplyType();
        String content = rule.getReplyContent();
        String appid = rule.getAppId();
        String openid = user.getOpenid();
        if (replyType == null) {
            log.error("特定关注回复、回复类型为空，appid={}，ruleid={}", appid, rule.getId());
            return;
        }
        if (SubReplyTypeEnum.text.getValue() == replyType) {
            // 文本
            content = replaceTextMsg(content, user);
            wxMsgService.sendCustomerTextMsg(appid, openid, content);
        } else if (SubReplyTypeEnum.image.getValue() == replyType) {
            // 图片
            wxMsgService.sendConsumerImgMsg(appid, openid, content);
        } else if (SubReplyTypeEnum.news.getValue() == replyType) {
            // 图文
            wxMsgService.sendConsumerMpNewsMsg(appid, openid, content);
        } else {
            log.error("特定关注回复、回复类型不匹配，appid={}，ruleid={}", appid, rule.getId());
        }
    }

    /**
     * 替换 昵称和广告
     *
     * @param replyData
     * @param user
     * @return
     */
    private String replaceTextMsg(String replyData, WxUserInfoExp user) {
        if (StringUtil.isEmpty(replyData)) {
            return StringUtil.EMPTY_STRING;
        }
        //替换固定字符串消息
        replyData = replyData.replaceAll(PlaceholderConstants.NICK_NAME_PLACEHOLDER, user.getNickname());
        //替换动态文案 !!advert!!
        AppRequest appRequest = new AppRequest();
        appRequest.setAppId(user.getAppId());
        // 查出appid下的所有广告
        List<WxSubscribeReplyAdvertDTO> data = adverseService.queryAdvert(appRequest).getData();
        if (!CollectionUtils.isEmpty(data)) {
            //重新洗牌
            Collections.shuffle(data);
            WxSubscribeReplyAdvertDTO replyAdvert = data.get(0);
            replyData = replyData.replaceAll(PlaceholderConstants.ADVERT, "\n" + replyAdvert.getAdvert());

            //替换url，追加openid+appid方便短链系统统计
            StringBuffer argsbuf = new StringBuffer();
            argsbuf.append("token=").append(getToken(user.getAppId(), user.getOpenid()));
            String regContent = RegHttpUtil.regContent(replyData, argsbuf.toString());
            return regContent;
        } else {
            replyData = replyData.replaceAll(PlaceholderConstants.ADVERT, StringUtil.EMPTY_STRING);
            return replyData;
        }
    }

    private String getToken(String appId, String openId) {
        long currentTimeMillis = System.currentTimeMillis();
        WgjSendLink wgjSendLink = new WgjSendLink();
//        String secret = jTemplateMsgObj.getTemplatePrimaryKey() + uId + currentTimeMillis + appConfig.getShortLink() + appId;
        StringBuffer secretBuf = new StringBuffer();
        String secret = secretBuf.append(appId).append(openId).append(shortLinkProperties.getKey()).toString();
        secret = DigestUtils.md5DigestAsHex(secret.getBytes(StandardCharsets.UTF_8));
        wgjSendLink.setToken(secret);
        wgjSendLink.setStampTime(String.valueOf(currentTimeMillis));
        wgjSendLink.setOpenId(openId);
        wgjSendLink.setAppId(appId);

        //加密后的token
        String encode = "";
        String jsonString = JSONObject.toJSONString(wgjSendLink);
        try {
            encode = new String(Base64.getEncoder().encode(jsonString.getBytes("utf-8")));
        } catch (UnsupportedEncodingException e2) {
            e2.printStackTrace();
        }
        return encode;
    }

    /**
     * 二维码关注进来的操作
     *
     * @param message
     */
    private boolean QRCodeReplay(EventMsg message) {
        String eventKey = message.getEventKey();
        String appId = message.getAppId();
        String openId = message.getFromUserName();
        log.info("qrcode event: " + openId + " subscribe " + message.getToUserName() + "." + eventKey);

        String sceneStr = eventKey.split("qrscene_")[1];
        if (StringUtils.isBlank(sceneStr)) {
            log.error("sceneStr 为空 ，eventKey={}", eventKey);
            return false;
        }
        //调用其他接口 通知立白
        threadPoolTaskExecutor.execute(() ->
                qrsceneNotifyTheUser(appId, openId, sceneStr)
        );

        // 回复二维码设置的消息
        // 获取用户信息
        WxUserInfoExp userInfo = userService.getUserInfo(appId, openId);
        // 回复消息
        return replyMsg(userInfo, sceneStr);
    }


    /**
     * 扫渠道二维码通知用户
     *
     * @param appId        公众号ID
     * @param fromUserName 用户openId
     * @param sceneStr     二维码渠道标识
     */
    private void qrsceneNotifyTheUser(String appId, String fromUserName, String sceneStr) {
        if (StringUtils.isNotBlank(sceneStr)) {
            NotifyUserActivityDTO notifyUserActivity = new NotifyUserActivityDTO();
            notifyUserActivity.setAppId(appId);
            notifyUserActivity.setOpenId(fromUserName);
            notifyUserActivity.setCodeType(sceneStr);
            log.info("SubscribeProcessor qrsceneNotifyTheUser notifyUserActivity=" + notifyUserActivity);
            externalSystemService.qrsceneNotifyTheUser(notifyUserActivity);
        }
    }

    /**
     * 向用户发送普通文本消息
     */
    private boolean replyTextMsg(String appId, String openId, String sceneStr) {
        WxQrcodeReplyQueryOneRequest wxQrcodeReplyQueryOneRequest = new WxQrcodeReplyQueryOneRequest();
        wxQrcodeReplyQueryOneRequest.setAppId(appId);
        wxQrcodeReplyQueryOneRequest.setSceneStr(sceneStr);
        WxQrcodeReplyInfoDTO wxQrcodeReplyInfo = wxParamQrcodeService.queryOneReplyInfo(wxQrcodeReplyQueryOneRequest).getData();

        if (null != wxQrcodeReplyInfo && StringUtils.isNotBlank(wxQrcodeReplyInfo.getContent())) {
            wxMsgService.sendCustomerTextMsg(appId, openId, wxQrcodeReplyInfo.getContent());
            //已回复
            return true;
        } else {
            log.error("回复二维码，未找到回复内容！！！request={}", wxQrcodeReplyQueryOneRequest);
        }
        return false;
    }

    /**
     * 向用户发送消息
     *
     * @param user
     * @param eventKey
     */
    private boolean replyMsg(WxUserInfoExp user, String eventKey) {
        WxQrcodeReplyQueryOneRequest request = new WxQrcodeReplyQueryOneRequest();
        String appId = user.getAppId();
        String openid = user.getOpenid();
        request.setAppId(appId);
        request.setSceneStr(eventKey);
        SimpleResult<WxQrcodeReplyInfoDTO> result = wxParamQrcodeService.queryOneReplyInfo(request);
        WxQrcodeReplyInfoDTO data = result.getData();
        log.error("QRCode subscribe reply data = {}", data);
        if (null == data || StringUtils.isBlank(data.getContent())) {
            log.info("未配置回复消息，参数二维码关注不回复消息！appid = {},eventKey = {},openid={},msg = {}", appId, eventKey, openid, result.getErrmsg());
            return false;
        }
        //回复消息
        if (CustomMsgTypeEnum.text.getCode().equals(data.getMsgType())) {
            // 文本
            String content = replaceTextMsg(data.getContent(), user);
            wxMsgService.sendCustomerTextMsg(appId, openid, content);
        } else if (CustomMsgTypeEnum.image.getCode().equals(data.getMsgType())) {
            // 发送图文消息
            wxMsgService.sendConsumerImgMsg(appId, openid, data.getContent());
        } else if (CustomMsgTypeEnum.mpnews.getCode().equals(data.getMsgType())) {
            // 发送 msnews 消息
            wxMsgService.sendConsumerMpNewsMsg(appId, openid, data.getContent());
        } else {
            log.error("QRCode reply error , The msg type undefined! {}", data);
        }
        return true;
    }

    @Override
    public String getEvent() {
        return EventTypeEnum.subscribe.getCode();
    }

}